package com.wsoft.bpm.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wsoft.bpm.constants.BpmConstant;
import com.wsoft.bpm.entity.ActWorkflowConfigEntity;
import com.wsoft.bpm.enums.GatewayType;
import com.wsoft.bpm.query.node.*;
import com.wsoft.bpm.service.IActWorkflowConfigService;
import com.wsoft.bpm.vo.VariableVO;
import com.wsoft.constant.CommonConstant;
import com.wsoft.constant.redis.RedisKeyConstant;
import com.wsoft.core.service.RedisService;
import com.wsoft.entity.BaseEntity;
import com.wsoft.response.ResponseResult;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.log4j.Log4j2;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Model;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

/**
 * @author zhonglj
 * @date 2024-09-19 16:05
 **/
@Log4j2
@RestController
@Tag(name = "BPMN节点定义")
@RequestMapping("/bpm/def")
public class BpmDefController {
    @Resource
    private IActWorkflowConfigService actWorkflowConfigService;
    @Resource
    private RedisService redisService;

    private void setNodeCacheBefore(BaseNode req, String type) {
        String id = StrUtil.isBlank(req.getId()) ? generateId(type) : req.getId();
        req.setId(id);
        req.setValidated(false);
        cacheJson(id, req.getModelId(), JSONUtil.toJsonStr(req));
    }

    private String setNodeCacheAfter(BaseNode req, String xmlData, String nodeTypeName) {
        // 判断id是否为前端传递
        boolean isFront = StrUtil.contains(req.getId(), "_");
        // 生成新的后端id，并使用新id作为缓存
        if (isFront) {
            String oldId = req.getId();
            // 删除原本缓存数据
            String key = StrUtil.format(RedisKeyConstant.MODEL_DATA_CACHE_KEY_TEMPLATE, req.getModelId(), oldId);
            redisService.del(key);
            // 缓存新id数据，标记验证通过
            String newId = generateId(nodeTypeName);
            cacheJson(newId, req.getModelId(), JSONUtil.toJsonStr(req.setId(newId).setValidated(true)));
            // 替换xml中的id
            return StrUtil.replace(xmlData, oldId, newId);
        } else {
            // 缓存原id数据，标记验证通过
            cacheJson(req.getId(), req.getModelId(), JSONUtil.toJsonStr(req.setValidated(true)));
            return xmlData;
        }

    }

    private String generateId(String name) {
//        Random random = new Random();
//        // 生成a-z字母
//        int firstChar = 97 + random.nextInt(26);
//        String firstCharHex = CharUtil.toString((char) firstChar);
//        // 生成剩余部分的UUID
//        String restOfUUID = IdUtil.fastSimpleUUID();
//        String restOfHex = restOfUUID.toString().substring(1); // 去掉第一个字符，因为我们已经定义了
//        String uuid = firstCharHex + restOfHex;
        return name + IdUtil.fastSimpleUUID();
    }

    private void cacheJson(String id, String modelId, String jsonData) {
        String key = StrUtil.format(RedisKeyConstant.MODEL_DATA_CACHE_KEY_TEMPLATE, modelId, id);
        // json数据暂存缓存,缓存一天时间
        redisService.set(key, jsonData, 24 * 3600);
    }

    @Operation(summary = "开始", description = "开始")
    @PostMapping("/startEvent")
    public ResponseResult<String> startEvent(@RequestBody @Validated StartEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_START_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlStartEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_START_EVENT));
    }

    @Operation(summary = "连接线", description = "连接线")
    @PostMapping("/sequenceFlow")
    public ResponseResult<String> sequenceFlow(@RequestBody @Validated SequenceFlowQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_SEQUENCE_FLOW);
        String xmlData = actWorkflowConfigService.convertXmlSequenceFlow(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_SEQUENCE_FLOW));
    }

    @Operation(summary = "网关", description = "网关")
    @PostMapping("/gateway")
    public ResponseResult<String> gateway(@RequestBody @Validated GateWayQuery req) {
        String type = null;
        if (StrUtil.equals(req.getType(), GatewayType.EVENT_BASED.getValue())) {
            type = BpmConstant.GENERATE_ID_PREFIX_EVENT_BASED_GATEWAY;
        }
        if (StrUtil.equals(req.getType(), GatewayType.EXCLUSIVE.getValue())) {
            type = BpmConstant.GENERATE_ID_PREFIX_EXCLUSIVE_GATEWAY;
        }
        if (StrUtil.equals(req.getType(), GatewayType.INCLUSIVE.getValue())) {
            type = BpmConstant.GENERATE_ID_PREFIX_INCLUSIVE_GATEWAY;
        }
        if (StrUtil.equals(req.getType(), GatewayType.PARALLEL.getValue())) {
            type = BpmConstant.GENERATE_ID_PREFIX_PARALLEL_GATEWAY;
        }

        setNodeCacheBefore(req, type);
        String xmlData = actWorkflowConfigService.convertXmlGateWay(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, type));
    }

    @Operation(summary = "用户任务", description = "用户任务")
    @PostMapping("/userTask")
    public ResponseResult<String> task(@RequestBody @Validated TaskQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_USERTASK);
        String xmlData = actWorkflowConfigService.convertXmlUserTask(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_USERTASK));
    }

    @Operation(summary = "获取流程变量", description = "获取流程变量")
    @PostMapping("/variable")
    public ResponseResult<List<VariableVO>> variable(@RequestBody @Validated VariableQuery req) {
        return ResponseResult.success(actWorkflowConfigService.variable(req));
    }

    @Operation(summary = "定时边界事件", description = "定时边界事件")
    @PostMapping("/timeBoundaryEvent")
    public ResponseResult<String> timeBoundaryEvent(@RequestBody @Validated TimeBoundaryEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_TIME_BOUNDARY_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlTimeBoundaryEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_TIME_BOUNDARY_EVENT));
    }

    @Operation(summary = "定时中间件事件", description = "定时中间件事件")
    @PostMapping("/timeIntermediateEvent")
    public ResponseResult<String> timeIntermediateEvent(@RequestBody @Validated TimeIntermediateEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_TIME_INTERMEDIATE_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlTimeIntermediateEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_TIME_INTERMEDIATE_EVENT));
    }

    @Operation(summary = "消息边界事件", description = "消息边界事件")
    @PostMapping("/messageBoundaryEvent")
    public ResponseResult<String> messageBoundaryEvent(@RequestBody @Validated MessageBoundaryEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_MESSAGE_BOUNDARY_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlMessageBoundaryEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_MESSAGE_BOUNDARY_EVENT));
    }

    @Operation(summary = "消息中间件事件", description = "消息中间件事件")
    @PostMapping("/messageIntermediateEvent")
    public ResponseResult<String> messageIntermediateEvent(@RequestBody @Validated MessageIntermediateEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_MESSAGE_INTERMEDIATE_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlMessageIntermediateEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_MESSAGE_INTERMEDIATE_EVENT));
    }

    @Operation(summary = "结束", description = "结束")
    @PostMapping("/endEvent")
    public ResponseResult<String> endEvent(@RequestBody @Validated EndEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_END_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlEndEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_END_EVENT));
    }

    @Operation(summary = "终止结束", description = "终止结束")
    @PostMapping("/terminateEndEvent")
    public ResponseResult<String> terminateEndEvent(@RequestBody @Validated TerminateEndEventQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_TERMINATE_END_EVENT);
        String xmlData = actWorkflowConfigService.convertXmlTerminateEndEvent(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_TERMINATE_END_EVENT));
    }

    @Resource
    private RepositoryService repositoryService;

    @Operation(summary = "处理流程", description = "处理流程")
    @PostMapping("/process")
    public ResponseResult<String> process(@RequestBody @Validated ProcessQuery req) {
        // 查询模版
        Model model = repositoryService.createModelQuery().modelId(req.getModelId()).singleResult();
//        req.setId(model.getKey());
        req.setName(model.getName());
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_PROCESS);
        String xmlData = actWorkflowConfigService.convertXmlProcess(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_PROCESS));
    }

    @Operation(summary = "内嵌子流程", description = "内嵌子流程")
    @PostMapping("/subProcess")
    public ResponseResult<String> subProcess(@RequestBody @Validated SubProcessQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_SUB_PROCESS);
        String xmlData = actWorkflowConfigService.convertXmlSubProcess(req);
        setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_SUB_PROCESS);
        return ResponseResult.success(xmlData);
    }

    @Operation(summary = "调用子流程", description = "调用子流程")
    @PostMapping("/callActivity")
    public ResponseResult<String> callActivity(@RequestBody @Validated CallActivityQuery req) {
        setNodeCacheBefore(req, BpmConstant.GENERATE_ID_PREFIX_CALL_ACTIVITY);
        String xmlData = actWorkflowConfigService.convertXmlCallActivity(req);
        return ResponseResult.success(setNodeCacheAfter(req, xmlData, BpmConstant.GENERATE_ID_PREFIX_CALL_ACTIVITY));
    }

    @Operation(summary = "退出流程设计器清理缓存", description = "退出流程设计器清理缓存")
    @GetMapping("/clearModelCache")
    public ResponseResult clearModelCache(@RequestParam String modelId) {
        actWorkflowConfigService.clearModelCache(modelId);
        return ResponseResult.success("[sys.msg.success]");
    }


    @Operation(summary = "删除模型节点", description = "根据modelId(模型id),activityId(节点id)删除节点数据")
    @GetMapping("/delete")
    public ResponseResult deleteNode(@RequestParam String modelId,
                                     @RequestParam String activityId) {
        // 删除缓存
        String key = StrUtil.format(RedisKeyConstant.MODEL_DATA_CACHE_KEY_TEMPLATE, modelId, activityId);
        redisService.del(key);

        // 数据库删除
        actWorkflowConfigService.remove(
                Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                        .eq(ActWorkflowConfigEntity::getModelId, modelId)
                        .eq(ActWorkflowConfigEntity::getActId, activityId)
                        .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
                        // 默认删除未部署数据
                        .isNull(ActWorkflowConfigEntity::getProcDefId)
        );

        return ResponseResult.success("[sys.msg.success]");
    }

    @Operation(summary = "获取节点json配置数据", description = "根据modelId(模型id),activityId(节点id)获取节点json配置数据")
    @GetMapping("/config")
    public ResponseResult<JSONObject> getConfigById(@RequestParam String modelId,
                                                    @RequestParam String activityId,
                                                    @RequestParam(required = false) String procDefId) {
        // 最新数据的保存首先从缓存获取
        String key = StrUtil.format(RedisKeyConstant.MODEL_DATA_CACHE_KEY_TEMPLATE, modelId, activityId);
        Object value = redisService.get(key);
        if (value != null) {
            return ResponseResult.success(JSONUtil.parseObj(value.toString()));
        }

        // 数据库获取
        ActWorkflowConfigEntity entity = actWorkflowConfigService.getOne(
                Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                        .eq(ActWorkflowConfigEntity::getModelId, modelId)
                        .eq(ActWorkflowConfigEntity::getActId, activityId)
                        .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
                        // 流程定义id是否为空来执行不同sql查询
                        .eq(StrUtil.isNotBlank(procDefId), ActWorkflowConfigEntity::getProcDefId, procDefId)
                        .isNull(StrUtil.isBlank(procDefId), ActWorkflowConfigEntity::getProcDefId)
        );

        if (entity == null || StrUtil.isBlank(entity.getJson())) {
            log.error("[bpm.def.error.empty.configJson]");
            return ResponseResult.success(new JSONObject());
        }

        return ResponseResult.success(JSONUtil.parseObj(entity.getJson()));
    }
}
